package internal

import (
	"github.com/name5566/leaf/gate"
	"strconv"
	"strings"
	"talent.com/agency_im/common/cxt"
	"talent.com/agency_im/common/interceptors"
	"talent.com/agency_im/common/msg"
	"talent.com/agency_im/conf/g"
	"talent.com/server/durotar.git/log"
	"talent.com/server/durotar.git/proto"
	"talent.com/server/durotar.git/tools/tz"
)

func getUserContext(ag gate.Agent) *cxt.UserContext {
	if ag == nil {
		return nil
	}
	if ag.UserData() == nil {
		return nil
	}
	if uc, ok := ag.UserData().(*cxt.UserContext); ok {
		return uc
	}
	return nil
}

func subscribeUser(userID uint64, targetUserID uint64) {
	v, ok := g.SubscribeUserMap[targetUserID]
	if ok {
		v[userID] = tz.GetNowTs()
	} else {
		g.SubscribeUserMap[targetUserID] = map[uint64]int64{userID: tz.GetNowTs()}
	}
}

func getSubscribers(targetUserID uint64) []uint64 {
	v, ok := g.SubscribeUserMap[targetUserID]
	if ok {
		var result []uint64
		for k := range v {
			result = append(result, k)
		}
	}
	return []uint64{}
}

func sendPresenceNT(ag gate.Agent, targetUserID uint64) {
	_, ok := g.AllAgents.Load(targetUserID)
	var presenceType msg.PRESENCE_TYPE
	if ok {
		presenceType = msg.PRESENCE_TYPE_ONLINE
	} else {
		presenceType = msg.PRESENCE_TYPE_OFFLINE
	}
	ag.WriteMsg(&msg.PresenceNT{
		ID:   targetUserID,
		Type: presenceType,
	})
}

func sendPresenceNT2Subscribers(targetUserID uint64, presenceType msg.PRESENCE_TYPE) {
	log.Info("broadcast present state for userID: [%v]", targetUserID)
	for sub := range getSubscribers(targetUserID) {
		if v, ok := g.AllAgents.Load(sub); ok {
			ag := v.(gate.Agent)
			ag.WriteMsg(&msg.PresenceNT{
				ID:   targetUserID,
				Type: presenceType,
			})
		}
	}
}

func wrapTopic(sourceID uint64, topicID string) *msg.Topic {
	ss := strings.Split(topicID, "-")
	var err error
	if len(ss) == 1 {
		if ID, err := strconv.ParseUint(topicID, 0, 64); err != nil {
			return nil
		} else {
			return &msg.Topic{TopicType: msg.TOPIC_TYPE_GROUP, ID: ID}
		}
	} else {
		var sid, tid uint64
		if sid, err = strconv.ParseUint(ss[0], 0, 64); err != nil {
			return nil
		}
		if tid, err = strconv.ParseUint(ss[1], 0, 64); err != nil {
			return nil
		}
		if sourceID == sid {
			return &msg.Topic{TopicType: msg.TOPIC_TYPE_PRIVATE, ID: tid}
		} else {
			return &msg.Topic{TopicType: msg.TOPIC_TYPE_PRIVATE, ID: sid}
		}
	}
}

func wrapMessages(messages *[]msg.Message) []*msg.Msg {
	result := make([]*msg.Msg, len(*messages))
	for i, m := range *messages {
		result[i] = &msg.Msg{
			Timestamp: int64(m.CreatedAt.Unix()),
			MsgID:     m.ID,
			ClientID:  m.ClientID,
			MsgType:   msg.MSG_TYPE(m.MsgType),
			Source:    m.SourceID,
			Info:      m.Info,
		}
	}
	return result
}

func notifyPushMessageByTopicID(topicID string) {
	log.Debug("Notify push messages by topic : %v", topicID)
	msgs := make(map[uint64][]msg.Message)
	err := msg.GetUnNotifiedMessagesByTopicID(topicID, &msgs)
	if err != nil {
		return
	}
	for userID, messages := range msgs {
		log.Debug("1 Push message to %v", userID)
		if value, ok := g.AllAgents.Load(userID); ok {
			log.Debug("2 Push message to %v", userID)
			ag := value.(gate.Agent)
			ag.WriteMsg(&msg.ChatNT{
				Topic:    wrapTopic(userID, topicID),
				MsgCount: int32(len(messages)),
				Msgs:     wrapMessages(&messages),
			})
		}
	}
}

func notifyPushMessageByUserID(userID uint64) {
	log.Debug("Notify push messages by userID : %v", userID)
	msgs := make(map[string][]msg.Message)
	err := msg.GetUnNotifiedMessagesByUserID(userID, &msgs)
	if err != nil {
		return
	}
	if value, ok := g.AllAgents.Load(userID); ok {
		ag := value.(gate.Agent)
		for topicID, messages := range msgs {
			ag.WriteMsg(&msg.ChatNT{
				Topic:    wrapTopic(userID, topicID),
				MsgCount: int32(len(messages)),
				Msgs:     wrapMessages(&messages),
			})
		}
	}
}

func SendMessage(message *msg.Message) error {
	if isExecSend, err := interceptors.ExecAllInterceptorsPreSendMessage(message); err != nil {
		log.Error("exec interceptors pre send message fail, msg: %+v, err: %v", message, err)
		return err
	} else {
		if isExecSend {
			err = msg.SaveMessage(message)
			if err == nil {
				notifyPushMessageByTopicID(message.TopicID)
			} else {
				return err
			}
			err = interceptors.ExecAllInterceptorsPostSendMessage(message)
			if err != nil {
				log.Error("exec interceptors post send message fail, msg: %+v, err: %v", message, err)
			}
			return nil
		}
		return nil
	}
}

func SendErrorMessage(userID uint64, errorMsg string) {
	if v, ok := g.AllAgents.Load(userID); ok {
		ag := v.(gate.Agent)
		ag.WriteMsg(&proto.ErrorST{
			Status: proto.STATUS_UNKNOWN_ERROR,
			Msg:    errorMsg,
		})
	}
}
